/* database functions
 *
 * Bot's database scheme:
 *
 *   %bot_directory/
 *   |  chats/                 - there is chats' preferences and data 
 *   |  |
 *   |  |  %chat_id%/          - naming of these directories
 *   |  |  |                     should be ONLY by chats' ID
 *   |  |  |
 *   |  |  |  chat_config.toml - chat's preferences 
 *
 */
package main

import (
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/naoina/toml"
)

type maidDB struct {
	Chats     map[string]*chat
}

type chat struct {
	Config    chatConfig
	Users     map[string]*user
}

type chatConfig struct {
	BanCommandOn                    bool
	DeleteLastWelcome               bool
	DisableWebPagePreview           bool
	WelcomeDisableWebPagePreview    bool
	RulesDisableWebPagePreview      bool
	HelpCommandOn                   bool
	InfoCommandOn                   bool
	MuteCommandOn                   bool
	RulesCommandOn                  bool
	WelcomeOn                       bool

	LastWelcomeID                   int
	RulesMsg                        string
	WelcomeMsg                      string

	BanCmd                          string
	ConfigCmd                       string
	HelpCmd                         string
	InfoCmd                         string
	KickCmd                         string
	MuteCmd                         string
	RemoveCmd                       string
	RulesCmd                        string
	SetCmd                          string
	SetrulesCmd                     string
	SetwelcomeCmd                   string
	StatusCmd                       string
	UnmuteCmd                       string
	UnsetCmd                        string
	UpdateCmd                       string
	WarnCmd                         string
	WelcomeCmd                      string

	WarnsLimit                      int
	// 0 - ban
	// 1 - kick
	// 2 - mute
	WarnsAction                     int
}

type user struct {
	PreferredName    string
	Gender           string
	Pronouns         string
	AdminNotes       string
	Notes            string
	BanNote          string
	BanFrom          string
	// 0   - member
	// 10  - can change user info
	// 50  - moderator
	// 70  - can set rules and welcome
	// 100 - full admin rights
	Privileges       int

	Warns            int
}

var Chat_cfg_defaults = chatConfig{
	BanCommandOn:        true,
	DeleteLastWelcome:   true,
	HelpCommandOn:       true,
	InfoCommandOn:       true,
	MuteCommandOn:       true,
	RulesCommandOn:      true,

	BanCmd:              "/ban",
	InfoCmd:             "/info",
	MuteCmd:             "/mute",
	KickCmd:             "/kick",
	WarnCmd:             "/warn",

	WarnsLimit:          3,
	LastWelcomeID:       0,
	WelcomeOn:           true,
	RulesMsg:
		"There's no rules yet. " +
		"Set rules by /setrules command.",
	WelcomeMsg:
		"Welcome!",
}

func dbInit() (maidDB, error) {
	db := maidDB{}
	db.Chats = make(map[string]*chat)
	var err error

	db_dir    := filepath.Join(config.BotDirectory, "db")
	chats_dir := filepath.Join(db_dir, "chats")

	if _, err = os.Stat(chats_dir); err != nil {
		if os.IsNotExist(err) {
			err = os.MkdirAll(chats_dir, 0700)
			if err != nil {
				return db, err
			}
		} else {
			return db, err
		}
	}

	// load chats' preferences into RAM
	chats_dirs, err := ioutil.ReadDir(chats_dir)
	if err != nil {
		return db, err
	}

	for _, chat_dir := range chats_dirs {
		chat_id       := chat_dir.Name()
		chat_dir_path := filepath.Join(chats_dir, chat_id)

		// check if chat's directory doesn't exist
		if _, err := os.Stat(chat_dir_path); err != nil {
				return db, err
		}

		chat_config := filepath.Join(chat_dir_path, "chat_config.toml")

		// check if chat's prefeneces file doesn't exist 
		if _, err := os.Stat(chat_config); err != nil {
			if os.IsNotExist(err) {
				_, err = os.Create(chat_config)
				if err != nil {
					return db, err
				}
			} else {
				return db, err
			}
		}

		data, err := ioutil.ReadFile(chat_config)
		if err != nil {
			return db, err
		}
		if string(data) != "" {
			f, err := os.Open(chat_config)
			if err != nil {
				return db, err
			}
			defer f.Close()

			// decode chat_config.toml
			db.Chats[chat_id] = new(chat)
			err = toml.NewDecoder(f).Decode(&db.Chats[chat_id].Config)
			if err != nil {
				return db, err
			}
		} else {
			f, err := os.Open(chat_config)
			if err != nil {
				return db, err
			}
			defer f.Close()

			db.Chats[chat_id].Config = Chat_cfg_defaults

			err = toml.NewEncoder(f).Encode(db.Chats[chat_id].Config)
			if err != nil {
				return db, err
			}
		}

		userdb := filepath.Join(chat_dir_path, "userdb.toml")

		// check if users database file doesn't exist 
		if _, err := os.Stat(userdb); err != nil {
			if os.IsNotExist(err) {
				_, err = os.Create(userdb)
				if err != nil {
					return db, err
				}
			} else {
				return db, err
			}
		}

		data, err = ioutil.ReadFile(userdb)
		if err != nil {
			return db, err
		}
		if string(data) != "" {
			f, err := os.Open(userdb)
			if err != nil {
				return db, err
			}
			defer f.Close()

			// decode userdb.toml
			err = toml.NewDecoder(f).Decode(&db.Chats[chat_id].Users)
			if err != nil {
				return db, err
			}
		}
	}
	return db, err
}

func dbWriteChatConfig(chat_id string, chat_cfg chatConfig) (error) {
	var err error = nil

	db_dir    := filepath.Join(config.BotDirectory, "db")
	chats_dir := filepath.Join(db_dir, "chats")

	chat_dir_path := filepath.Join(chats_dir, chat_id)

	if _, err = os.Stat(chat_dir_path); err != nil {
		if os.IsNotExist(err) {
			err = os.MkdirAll(chat_dir_path, 0700)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	}

	chat_cfg_path := filepath.Join(chat_dir_path, "chat_config.toml")

	f, err := os.Create(chat_cfg_path)
	if err != nil {
		return err
	}

	err = toml.NewEncoder(f).Encode(chat_cfg)
	if err != nil {
		return err
	}

	return err
}

func dbWriteChatUsers(chat_id string, users map[string]*user) (error) {
	var err error = nil

	db_dir    := filepath.Join(config.BotDirectory, "db")
	chats_dir := filepath.Join(db_dir, "chats")

	chat_dir_path := filepath.Join(chats_dir, chat_id)

	if _, err = os.Stat(chat_dir_path); err != nil {
		if os.IsNotExist(err) {
			err = os.MkdirAll(chat_dir_path, 0700)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	}

	chat_cfg_path := filepath.Join(chat_dir_path, "userdb.toml")

	f, err := os.Create(chat_cfg_path)
	if err != nil {
		return err
	}

	err = toml.NewEncoder(f).Encode(users)
	if err != nil {
		return err
	}

	return err
}
